/* Copyright 2008 Bas van den Berg
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "MudObject.h"
#include "Room.h"
#include "CmdVisitor.h"
#include "Error.h"

using namespace Chronos;
using namespace std;

namespace Chronos {

struct MudObjectTriggerAction {
    MudObjectTrigger trigger;
    std::string listener;
    MudTriggerAction action;
    std::string msg;
    bool blocks;
    MudObjectTriggerAction(MudObjectTrigger t_, const std::string& l_, MudTriggerAction a_, const std::string& m_, bool b_)
        : trigger(t_)
        , listener(l_)
        , action(a_)
        , msg(m_)
        , blocks(b_)
    {}
};

}

MudObject::MudObject(const string& name_, const string& descr_)
    : name(name_)
    , descr(descr_)
    , visible(true)
    , room(0)
{
    ++count;
}


MudObject::MudObject(const MudObject& rhs)
    : name(rhs.name)
    , descr(rhs.descr)
    , visible(rhs.visible)
    , room(0)
{
    for (TriggerActionsConstIter iter = rhs.triggers.begin(); iter != rhs.triggers.end(); ++iter) {
        MudObjectTriggerAction* ta = *iter;
        addTrigger(ta->trigger, ta->listener, ta->blocks, ta->action, ta->msg);
    }
    for (AliasesIter iter = rhs.aliases.begin(); iter != rhs.aliases.end(); ++iter) {
		addAlias(*iter);
	}
    ++count;
}


MudObject::~MudObject() {
    generateEvent(OBJECT_DESTRUCTION);

    for (TriggerActionsConstIter iter = triggers.begin(); iter != triggers.end(); ++iter) {
        delete *iter;
    }
    --count;
}


unsigned int MudObject::count = 0;


void MudObject::addAlias(const std::string& alias) {
	aliases.push_back(alias);
}


bool MudObject::matchesName(const std::string& callName) const {
	if (name == callName) return true;
    AliasesIter iter = find(aliases.begin(), aliases.end(), callName);
	if (iter == aliases.end()) return false;
	return true;
}


VISITOR_ACCEPT(MudObject)

const string& MudObject::getName() const { return name; }

const string& MudObject::getDescription() const { return descr; }

bool MudObject::isVisible() const { return visible; }

void MudObject::setVisible(bool v) { visible = v; }

void MudObject::setRoom(Room* newRoom) { room = newRoom; }

Room* MudObject::getRoom() const { return room; }


void MudObject::addListener(MudObjectListener& listener) {
    ListenersIter iter = find(listeners.begin(), listeners.end(), &listener);
    if (iter != listeners.end()) {
   		throw MudError("MudObject [" + getName() + "]: listener already listening");
    }
    listeners.push_back(&listener);
}


void MudObject::removeListener(MudObjectListener& listener) {
    ListenersIter iter = find(listeners.begin(), listeners.end(), &listener);
    if (iter == listeners.end()) {
   		throw MudError("MudObject [" + getName() + "]: listener is not listening");
	}
    listeners.erase(iter); 
}


void MudObject::generateEvent(MudObjectEvent event) {
    ListenersIter iter = listeners.begin();
    while (iter != listeners.end()) {
        bool keep = (*iter)->handleObjectEvent(*this, event);
        if (!keep) iter = listeners.erase(iter);
        else ++iter;
    }
}


void MudObject::addTrigger(MudObjectTrigger trigger, const string& listener, bool blocks, MudTriggerAction action, const string& msg) {
    triggers.push_back(new MudObjectTriggerAction(trigger, listener, action, msg, blocks));
}


bool MudObject::activateTrigger(MudObjectTrigger trigger, MudMobile* cause) {
    bool blocked = false;
    for (TriggerActionsConstIter iter = triggers.begin(); iter != triggers.end(); ++iter) {
        MudObjectTriggerAction* ta = *iter;
        if (ta->trigger == trigger) {
            MudObject* object = room->findObject(ta->listener); 
            if (object == 0) continue;
            object->handleTrigger(ta->action, cause, ta->msg);
            blocked |= ta->blocks;
        }
    }
    return blocked;
}


void MudObject::clearTrigger(MudObjectTrigger trigger) {
    TriggerActionsIter iter = triggers.begin();
	while (iter != triggers.end()) {
        if ((*iter)->trigger == trigger) {
			delete *iter;
			iter = triggers.erase(iter);
		} else ++iter;
	}
}


